package com.example.wac.service.impl;

import com.example.wac.component.HttpHelper;
import com.example.wac.constant.Constant;
import com.example.wac.entity.WeChatMessageResponse.TextMessageResponse;
import com.example.wac.entity.common.ReqDTO;
import com.example.wac.enums.ErrorCode;
import com.example.wac.enums.WeChatUrlEnum;
import com.example.wac.service.WeChatService;
import com.example.wac.util.WeChatMessageXMLUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author wuancheng
 * @description
 * @date 2022-07-25 15:36:00
 */

@Slf4j
@Service
public class WeChatServiceImpl implements WeChatService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    HttpHelper httpHelper;

    @Override
    public String getAccessToken() {
        String accessToken = (String) redisTemplate.opsForValue().get(Constant.RedisKey.ACCESS_TOKEN_KEY);
        if (StringUtils.isBlank(accessToken)) {
            Map<String, Object> param = new HashMap<>(2);
            param.put("appid", Constant.WeChat.appId);
            param.put("appSecret", Constant.WeChat.appSecret);
            Map<String, Object> resultMap = httpHelper.getForObject(param, WeChatUrlEnum.GET_ACCESS_TOKEN);
            accessToken = MapUtils.getString(resultMap, "access_token");
            redisTemplate.opsForValue().set(Constant.RedisKey.ACCESS_TOKEN_KEY, accessToken, 7000, TimeUnit.SECONDS);
        }
        return accessToken;
    }

    @Override
    public Map<String, Object> getAccessInfoByWxCode(String wxCode) {
        String appId =  Constant.WeChat.appId;
        String appSecret = Constant.WeChat.appSecret;
        Map<String, Object> map = (Map<String, Object>) redisTemplate.opsForValue().get(Constant.RedisKey.KEY_WECHAT_CODE_OPENID + Constant.WeChat.appId + ":" + wxCode);
        // 若命中缓存，则直接返回openid，可在短时间内重复利用微信code与openid的关系
        if (org.apache.commons.collections4.MapUtils.isNotEmpty(map) && map.containsKey("openid") && StringUtils.isNotBlank((String) map.get("openid"))) {
            return map;
        }
        // 若未命中缓存，则调用微信接口获取网页授权accessToken和openid
        Map<String, Object> param = new HashMap<>(2);
        param.put("appid", appId);
        param.put("appSecret", appSecret);
        param.put("code", wxCode);
        Map<String, Object> resultMap = httpHelper.getForObject(param, WeChatUrlEnum.GET_ACCESS_TOKEN_BY_AUTHORIZE);
        if (null != resultMap && null == resultMap.get("errcode")) {
            // 临时短暂缓存微信code与openid的关系，避免静默授权前端重复刷新页面
            redisTemplate.opsForValue().set(Constant.RedisKey.KEY_WECHAT_CODE_OPENID + Constant.WeChat.appId + ":" + wxCode, 300);
        }
        return resultMap;
    }

    @Override
    public Map<String, Object> getWeChatUserInfoByWxCode(String wxCode) {
        Map<String, Object> map = this.getAccessInfoByWxCode(wxCode);
        String accessToken = (String) map.get("access_token");
        String openid = (String) map.get("openid");
        Map<String, Object> param = new HashMap<>(2);
        param.put("access_token", accessToken);
        param.put("openid", openid);
        Map<String, Object> resultMap = httpHelper.getForObject(param, WeChatUrlEnum.GET_USER_INFO_BY_AUTHORIZE);
        return resultMap;
    }

    /**
     * 校验签名
     *
     * @param signature 签名
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @return
     */
    @Override
    public boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[]{Constant.WeChat.token, timestamp, nonce};
        // 将token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        content = null;
        // 将sha1加密后的字符串可与signature对比，标识该请求来源于微信
        System.out.println(tmpStr.equals(signature.toUpperCase()));
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    }

    @Override
    public String processRequest(HttpServletRequest request) {
        String respXml = null;
        String respContent = "未知的消息类型!";
        try {
            // 解析xml获取参数map
            Map<String, String> requestMap = WeChatMessageXMLUtil.parseXml(request);

            // 取出基本参数 ToUserName-开发者微信号 FromUserName-发送方的openid MsgType-消息类型
            String fromUserName = requestMap.get("FromUserName");
            String toUserName = requestMap.get("ToUserName");
            String msgType = requestMap.get("MsgType");
            // 对所有的类型都回复文本消息
            TextMessageResponse textMessageResponse = new TextMessageResponse();
            textMessageResponse.setFromUserName(toUserName);
            textMessageResponse.setToUserName(fromUserName);
            textMessageResponse.setMsgType(Constant.WeChat.Message.RESP_MESSAGE_TYPE_TEXT);
            textMessageResponse.setCreateTime(System.currentTimeMillis());
            // 判断消息类型,获取对应的返回消息
            textMessageResponse.setContent(setRespContent(requestMap));

            // 转换为xml
            respXml = WeChatMessageXMLUtil.messageToXml(textMessageResponse);
            log.info("处理结束,返回参数为: {}", respXml);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return respXml;
    }

    @Override
    public Map<String, Object> sendTemplateMessage(ReqDTO dto) {
        HashMap<Object, Object> paramUrlMap = new HashMap<>(2);
        String accessToken = getAccessToken();
        paramUrlMap.put("access_token", accessToken);
        Map<String, Object> resultMap = httpHelper.postForObject(paramUrlMap, dto, WeChatUrlEnum.SEND_TEMPLATE_MESSAGE);
        return resultMap;
    }

    @Override
    public Map<String, Object> getWeChatUserInfo(String openid) {
        Map<String, Object> param = new HashMap<>(2);
        param.put("access_token", getAccessToken());
        param.put("openid", openid);
        Map<String, Object> resultMap = httpHelper.getForObject(param, WeChatUrlEnum.GET_USER_INFO);
        return resultMap;
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param byteArray
     * @return
     */
    private String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    /**
     * 将字节转换为十六进制字符串
     *
     * @param mByte
     * @return
     */
    private String byteToHexStr(byte mByte) {
        char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];

        String s = new String(tempArr);
        return s;
    }

    public static String setRespContent(Map<String, String> requestMap) {
        String respContent = null;
        String msgType = requestMap.get("MsgType");
        String content = requestMap.get("Content");
        // 判断是事件还是消息
        if (msgType.equals(Constant.WeChat.Message.REQ_MESSAGE_TYPE_EVENT)) {
            // 如果是事件,则多一个属性eventType
            String eventType = requestMap.get("Event");
            if (eventType.equals(Constant.WeChat.Message.EVENT_TYPE_SUBSCRIBE)) {
                respContent = "谢谢您的关注!";
            } else if (eventType.equals(Constant.WeChat.Message.EVENT_TYPE_UNSUBSCRIBE)) {
                // 用户取消关注后不用回复消息.
            } else if (eventType.equals(Constant.WeChat.Message.EVENT_TYPE_SCAN)) {
                // 扫描带参数的二维码
            } else if (eventType.equals(Constant.WeChat.Message.EVENT_TYPE_LOCATION)) {
                // 上报地理位置事件
            } else if (eventType.equals(Constant.WeChat.Message.EVENT_TYPE_CLICK)) {
                // 处理自定义菜单点击事件
            }
        } else {
            if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_TEXT)) {
                if (Objects.equals(content, "你好")){
                    respContent = "你好啊朋友";
                }else {
                    respContent = "杨样云是傻子";
                }
            } else if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_IMAGE)) {
                respContent = "图片消息~~~~~~!";
            } else if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_VOICE)) {
                respContent = "语音消息~~~~~~!";
            } else if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_VIDEO)) {
                respContent = "视频消息~~~~~~!";
            } else if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_MUSIC)) {
                respContent = "音乐消息~~~~~~!";
            } else if (msgType.equals(Constant.WeChat.Message.RESP_MESSAGE_TYPE_ARTICLE)) {
                respContent = "图文消息~~~~~~!";
            }
        }
        return respContent;
    }
}
